import { IRecoverable } from "./IRecoverable";

/**
 * 对象池
 */
export class Pool<T extends IRecoverable>
{
    /**池中闲置对象 */
    private _cacheStack: Array<T> = new Array<T>();
    /**正在使用的对象 */
    private _usingArray: Array<T> = new Array<T>();
    /**池中对象最大数 */
    private _maxCount: number = 0;

    private _class: { new(...arg): T };
    
    constructor(clazz: { new(...arg): T }, maxCount?: number) {
        this._class = clazz;
        if (!this._class) {
            throw new Error("构造函数不能为空！");
        }
        this._maxCount = maxCount == undefined ? Number.MAX_SAFE_INTEGER : maxCount;
    }

    /**
    * 在池中的对象
    */
    public get count(): number {
        return this._cacheStack.length;
    }

    /**
     * 使用中的数量
     */
    public get usingCount(): number {
        return this._usingArray.length;
    }

    /**
     * 分配
     * @returns 
     */
    public allocate(createData?: any): T {
        if (this.count + this.usingCount < this._maxCount) {
            let element: T = this._cacheStack.length > 0 ? this._cacheStack.pop()! : (typeof createData === 'function' ? createData() : new this._class(createData));
            this._usingArray.push(element);
            return element;
        }
        throw new Error("对象池最大数量超出：" + this._maxCount);
    }

    /**
     * 回收到池中
     * @param value 
     * @returns 
     */
    public recover(value: T): void {
        if (this._cacheStack.indexOf(value) > -1) {
            throw new Error("重复回收！");
        }
        let index = this._usingArray.indexOf(value);
        if (index < 0) {
            throw new Error("对象不属于改对象池！");
        }
        //重置
        value.reset();

        this._usingArray.splice(index, 1);
        this._cacheStack.push(value);
    }

    /**
     * 批量回收
     * @param list 
     */
    public recoverList(list: Array<T>): void {
        for (let index = 0; index < list.length; index++) {
            const element = list[index];
            this.recover(element);
        }
    }

    /**
     * 将所有使用中的对象都回收到池中
     */
    public recoverAll(): void {
        for (let index = 0; index < this._usingArray.length; index++) {
            const element = this._usingArray[index];
            this.recover(element);
        }
    }

    public destroy(): void {
        this.recoverAll();
        for (let index = 0; index < this._cacheStack.length; index++) {
            const element = this._cacheStack[index];
            element.destroy();
        }

        this._cacheStack.length = 0;
        this._cacheStack = null!;
        this._usingArray.length = 0;
        this._usingArray = null!;
    }
}
